//-----------------------------------------------------------------------------
// Copyright 2008 Andrés M. R. Martano
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>
//-----------------------------------------------------------------------------

#include "fogo.h"
#include "listaC.h"

SDL_Surface *tela = NULL;
int COMPRIMENTO_TELA = 600;
int ALTURA_TELA = 600;
int CORES_TELA = 32;
int TELA_CHEIA = 0;
int FRAMES_SEGUNDO = 20;
int NEBLINA = 0;

float Ox = 0;
float Oy = 0;
float Oz = 0;
#define N_TEXTURAS 10

//Câmera
camera cam;

//Lista onde serão colocadas as coisas a serem desenhadas
tabela coisas;

/* Ambient Light Values ( NEW ) */
GLfloat LightAmbient[]  = { 0.5f, 0.5f, 0.5f, 100.0f };
/* Diffuse Light Values ( NEW ) */
GLfloat LightDiffuse[]  = { 1.0f, 1.0f, 1.0f, 100.0f };
/* Light Position ( NEW ) */
GLfloat LightPosition[] = { 0.0f, 0.0f, -2.0f, 100.0f };

typedef struct													// Create A Structure
{
	GLubyte	*imageData;											// Image Data (Up To 32 Bits)
	GLuint	bpp;												// Image Color Depth In Bits Per Pixel.
	GLuint	width;												// Image Width
	GLuint	height;												// Image Height
	GLuint	texID;												// Texture ID Used To Select A Texture
} TextureImage;													// Structure Name

TextureImage textures[N_TEXTURAS]; /* Storage for N_TEXTURAS textures */

GLfloat fogColor[4]= {0.65f, 0.65f, 0.65f, 1.0f};				// Fog Color

int LoadTGA(TextureImage *texture, char *filename)				// Loads A TGA File Into Memory
{
	GLubyte		TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};		// Uncompressed TGA Header
	GLubyte		TGAcompare[12];									// Used To Compare TGA Header
	GLubyte		header[6];										// First 6 Useful Bytes From The Header
	GLuint		bytesPerPixel;									// Holds Number Of Bytes Per Pixel Used In The TGA File
	GLuint		imageSize;										// Used To Store The Image Size When Setting Aside Ram
	GLuint		temp;											// Temporary Variable
	GLuint		type=GL_RGBA;									// Set The Default GL Mode To RBGA (32 BPP)

	FILE *file = fopen(filename, "rb");							// Open The TGA File

	if(	file==NULL ||											// Does File Even Exist?
		fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) ||	// Are There 12 Bytes To Read?
		memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0				||	// Does The Header Match What We Want?
		fread(header,1,sizeof(header),file)!=sizeof(header))				// If So Read Next 6 Header Bytes
		{
			if (file != NULL) fclose(file);									// If Anything Failed, Close The File

			return 0;													// Return False
		}

		texture->width  = header[1] * 256 + header[0];					// Determine The TGA Width	(highbyte*256+lowbyte)
		texture->height = header[3] * 256 + header[2];					// Determine The TGA Height	(highbyte*256+lowbyte)

		if( texture->width <= 0 || texture->height <= 0 || (header[4]!=24 && header[4]!=32) )
		{
			fclose(file);												// If Anything Failed, Close The File
			return 0;													// Return False
		}

		texture->bpp = header[4];										// Grab The TGA's Bits Per Pixel (24 or 32)
		bytesPerPixel = texture->bpp/8;									// Divide By 8 To Get The Bytes Per Pixel
		imageSize = texture->width*texture->height*bytesPerPixel;		// Calculate The Memory Required For The TGA Data

		texture->imageData = (GLubyte *)malloc(imageSize);				// Reserve Memory To Hold The TGA Data

		if( texture->imageData==NULL || fread(texture->imageData, 1, imageSize, file)!=imageSize) // Does The Image Size Match The Memory Reserved?
		{
			if(texture->imageData!=NULL)								// Was Image Data Loaded
			{
				free(texture->imageData);								// If So, Release The Image Data
				texture->imageData = NULL;
			}

			fclose(file);												// Close The File
			return 0;													// Return False
		}

		for(GLuint i=0; i<(unsigned int)imageSize; i+=bytesPerPixel)	// Loop Through The Image Data
		{																// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
			temp=texture->imageData[i];									// Temporarily Store The Value At Image Data 'i'
			texture->imageData[i] = texture->imageData[i + 2];			// Set The 1st Byte To The Value Of The 3rd Byte
			texture->imageData[i + 2] = temp;							// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
		}

		fclose (file);													// Close The File

		// Build A Texture From The Data
		glGenTextures(1, &texture[0].texID);							// Generate OpenGL texture IDs

		glBindTexture(GL_TEXTURE_2D, texture[0].texID);					// Bind Our Texture

		//Tipos: GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		if (texture[0].bpp==24)											// Was The TGA 24 Bits
		{
			type=GL_RGB;												// If So Set The 'type' To GL_RGB
		}

		glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);

		//gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texture[0].width, texture[0].height, type, GL_UNSIGNED_BYTE, texture[0].imageData); // CONSTROE BITMAPS


		return 1;														// Texture Building Went Ok, Return True
}

/* function to reset our viewport after a window resize */
int resizeWindow( int width, int height )
{
	/* Height / width ration */
	GLfloat ratio;

	/* Protect against a divide by zero */
	if ( height == 0 )
	height = 1;

	ratio = ( GLfloat )width / ( GLfloat )height;

	/* Setup our viewport. */
	glViewport( 0, 0, ( GLint )width, ( GLint )height );

	/* change to the projection matrix and set our viewing volume. */
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity( );

	/* Set our perspective */
	gluPerspective( 45.0f, ratio, 0.1f, 100000.0f );

	/* Make sure we're chaning the model view and not the projection */
	glMatrixMode( GL_MODELVIEW );

	/* Reset The View */
	glLoadIdentity( );

	return 1;
}

void desenhar_2D( int textura, posicao pos, float tama )
{
	glLoadIdentity();

	//cam.posicionar2();
	cam.posicionar();

    glTranslatef( Ox, Oy, Oz );

	glTranslatef( pos.x*TAMANHO, pos.y*TAMANHO, pos.z*TAMANHO );
	glRotatef(pos.zr,0.0,0.0,1.0);
	glRotatef(pos.xr,1.0,0.0,0.0);

	glBindTexture( GL_TEXTURE_2D, textures[textura].texID );
	glBegin( GL_QUADS );
	glNormal3f( 0.0f, 0.0f, 1.0f );
	//XX
	//OX
	glTexCoord2f( 0.0f, 0.0f ); glVertex3f( -0.5f*tama*TAMANHO, -0.5f*tama*TAMANHO, 0.0f );
	//XX
	//XO
	glTexCoord2f( 1.0f, 0.0f ); glVertex3f( 0.5f*tama*TAMANHO, -0.5f*tama*TAMANHO, 0.0f );
	//XO
	//XX
	glTexCoord2f( 1.0f, 1.0f ); glVertex3f( 0.5f*tama*TAMANHO, 0.5f*tama*TAMANHO, 0.0f );
	//OX
	//XX
	glTexCoord2f( 0.0f, 1.0f ); glVertex3f( -0.5f*tama*TAMANHO, 0.5f*tama*TAMANHO, 0.0f );
	glEnd( );
}

void controlar_frame()
{
	static Uint32 tempo = 0;
 	//static Uint32 tempo2 = 0;

	tempo = SDL_GetTicks() - tempo;
	//cout << "(Tempo:" << tempo << ")\n";
	//cout << "(FPS:" << 1000/(float)tempo << ")\n";
	if( (int)(tempo) < 1000 / FRAMES_SEGUNDO )
	{
		SDL_Delay( ( 1000 / FRAMES_SEGUNDO ) - tempo );
	}

 	//tempo2 = SDL_GetTicks() - tempo2;
 	//cout << "(Tempo2:" << tempo2 << ")\n";
 	//cout << "(FPS2:" << 1000/(float)tempo2 << ")\n";
 	//tempo2 = SDL_GetTicks();

	tempo = SDL_GetTicks();
}

int carregar_textura( const char *nome, int pos )
{
	if ( !LoadTGA(&textures[pos], (char*)nome ) )
        return 0;
    else
        return 1;
}

int iniciar_GL()
{
	/* Enable Texture Mapping ( NEW ) */
	glEnable( GL_TEXTURE_2D );
	/* Enable smooth shading */
	glShadeModel( GL_SMOOTH );
	/* Set the background black */
	//glClearColor( 0.0f, 0.0f, 0.25f, 0.0f ); //Azul escuro
	glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
	/* Depth buffer setup */
	glClearDepth( 1.0f );
	/* Enables Depth Testing */
	glEnable( GL_DEPTH_TEST );
	/* The Type Of Depth Test To Do */
	glDepthFunc( GL_LEQUAL );
	/* Really Nice Perspective Calculations */
	glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );

 	///* Setup The Ambient Light */
 	//glLightfv( GL_LIGHT1, GL_AMBIENT, LightAmbient );
 	///* Setup The Diffuse Light */
 	//glLightfv( GL_LIGHT1, GL_DIFFUSE, LightDiffuse );
 	///* Position The Light */
 	//glLightfv( GL_LIGHT1, GL_POSITION, LightPosition );
 	///* Enable Light One */
 	//glEnable( GL_LIGHT1 );

// 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			// Enable Alpha Blending (disable alpha testing)
// 	glEnable(GL_BLEND);											// Enable Blending       (disable alpha testing)
		glAlphaFunc(GL_GREATER,0.1f);							// Set Alpha Testing     (disable blending)
		glEnable(GL_ALPHA_TEST);								// Enable Alpha Testing  (disable blending)
// 	glEnable(GL_CULL_FACE);										// Remove Back Face

	if( NEBLINA )
	{
		glClearColor(fogColor[0],fogColor[1],fogColor[2],fogColor[3]);
		//Tipos de neblina: GL_EXP, GL_EXP2, GL_LINEAR
		glFogi(GL_FOG_MODE, GL_EXP);		        // Fog Mode
		glFogfv(GL_FOG_COLOR, fogColor);			// Set Fog Color
		glFogf(GL_FOG_DENSITY, 0.020f/TAMANHO);		// How Dense Will The Fog Be
		glHint(GL_FOG_HINT, GL_NICEST);		    	// Fog Hint Value
		glFogf(GL_FOG_START, 5.0f);			    	// Fog Start Depth
		glFogf(GL_FOG_END, 200.0f);			    	// Fog End Depth
		glEnable(GL_FOG);				        	// Enables GL_FOG
	}

	return 1;
}

char iniciar_SDL_fogo()
{
	/* Flags to pass to SDL_SetVideoMode */
	int videoFlags;
	/* this holds some info about our display */
	const SDL_VideoInfo *videoInfo;
	/* whether or not the window is active */
	//int isActive = 1;

	/* initialize SDL */
	if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 )
	{
		fprintf( stderr, "Video initialization failed: %s\n",SDL_GetError( ) );
		return 1;
	}

	/* Fetch the video info */
	videoInfo = SDL_GetVideoInfo( );

	if ( !videoInfo )
	{
		fprintf( stderr, "Video query failed: %s\n", SDL_GetError( ) );
		return 1;
	}

	/* the flags to pass to SDL_SetVideoMode */
	videoFlags  = SDL_OPENGL;          /* Enable OpenGL in SDL */
	videoFlags |= SDL_GL_DOUBLEBUFFER; /* Enable double buffering */
	videoFlags |= SDL_HWPALETTE;       /* Store the palette in hardware */
	videoFlags |= SDL_RESIZABLE;       /* Enable window resizing */

	/* This checks to see if surfaces can be stored in memory */
	if ( videoInfo->hw_available )
		videoFlags |= SDL_HWSURFACE;
	else
		videoFlags |= SDL_SWSURFACE;

	/* This checks if hardware blits can be done */
	if ( videoInfo->blit_hw ) videoFlags |= SDL_HWACCEL;

	/* Sets up OpenGL double buffering */
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

	/* get a SDL surface */
	tela = SDL_SetVideoMode( COMPRIMENTO_TELA, ALTURA_TELA, CORES_TELA, videoFlags );

	/* Verify there is a surface */
	if ( !tela )
	{
		fprintf( stderr, "Video mode set failed: %s\n", SDL_GetError( ) );
		return 0;
	}

	/* initialize OpenGL */
	iniciar_GL();

	/* resize the initial window */

	if( TELA_CHEIA )
	{
		resizeWindow( 1024, 768 );
		SDL_WM_ToggleFullScreen( tela );
	}
	else
	{
		resizeWindow( COMPRIMENTO_TELA, ALTURA_TELA );
	}

	return 1;
}
//-----------------------------------------------------------------------------


//Limpa o buffer
char desenhar_listas_inicio()
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	return 1;
}
//-----------------------------------------------------------------------------

//Desenha todas as coisas na tela
char desenhar_lista( int coluna, float x, float y, float z )
{
	void *lin = NULL;
	void *objeto;

	Ox = x;
	Oy = y;
	Oz = z;

	do
	{
		objeto = retornar_objeto( &coisas, coluna, &lin );
		if(objeto != NULL)
		{
			((coisa*)objeto)->desenhar();
		}
	}
	while( lin != NULL );

	Ox = 0;
	Oy = 0;
	Oz = 0;

	return 1;
}
//-----------------------------------------------------------------------------


//Troca o buffer
char desenhar_listas_fim()
{
	SDL_GL_SwapBuffers( );

	controlar_frame();

	return 1;
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//Objetos a serem colocados na lista de desenho
//-----------------------------------------------------------------------------
//Coisa - objeto base
coisa::coisa( int coluna )
{
	inserir_linha(&coisas, coluna, sizeof(this), this);
}
coisa::~coisa()
{
	remover_linha(&coisas, coluna, this);
}

//Um plano 2D
plano::plano( posicao pos2, float tama, int dese, int coluna ) : coisa(coluna)
{
	pos.igualar(pos2);
	tamanho = tama;
	desenho = dese;
}

void plano::desenhar(void)
{
	desenhar_2D( desenho, pos, tamanho );
}
//-----------------------------------------------------------------------------

//Carrega a biblioteca
char acender_fogo( int colunas )
{
	if( !( iniciar_tabela( &coisas, colunas ) ) )return 0;
	if( !( iniciar_SDL_fogo() ) )return 0;
	cam.iniciar();

	return 1;
}

//Descarrega a biblioteca
char apagar_fogo()
{
	SDL_QuitSubSystem( SDL_INIT_VIDEO | SDL_INIT_TIMER );
	deletar_tabela(&coisas); //      *Falta remover coluna no final*

	return 1;
}

